/*
 * Copyright 2011-2017 Blender Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "stdosl.h"
#include "node_fresnel.h"

shader node_principled_bsdf(string distribution = "Multiscatter GGX",
                            string subsurface_method = "burley",
                            color BaseColor = color(0.8, 0.8, 0.8),
                            float Subsurface = 0.0,
                            vector SubsurfaceRadius = vector(1.0, 1.0, 1.0),
                            color SubsurfaceColor = color(0.7, 0.1, 0.1),
                            float Metallic = 0.0,
                            float Specular = 0.5,
                            float SpecularTint = 0.0,
                            float Roughness = 0.5,
                            float Anisotropic = 0.0,
                            float AnisotropicRotation = 0.0,
                            float Sheen = 0.0,
                            float SheenTint = 0.5,
                            float Clearcoat = 0.0,
                            float ClearcoatRoughness = 0.03,
                            float IOR = 1.45,
                            float Transmission = 0.0,
                            float TransmissionRoughness = 0.0,
                            normal Normal = N,
                            normal ClearcoatNormal = N,
                            normal Tangent = normalize(dPdu),
                            output closure color BSDF = 0)
{
  float f = max(IOR, 1e-5);
  float diffuse_weight = (1.0 - clamp(Metallic, 0.0, 1.0)) * (1.0 - clamp(Transmission, 0.0, 1.0));
  float final_transmission = clamp(Transmission, 0.0, 1.0) * (1.0 - clamp(Metallic, 0.0, 1.0));
  float specular_weight = (1.0 - final_transmission);

  vector T = Tangent;

  float m_cdlum = luminance(BaseColor);
  color m_ctint = m_cdlum > 0.0 ? BaseColor / m_cdlum :
                                  color(0.0, 0.0, 0.0);  // normalize lum. to isolate hue+sat

  /* rotate tangent */
  if (AnisotropicRotation != 0.0)
    T = rotate(T, AnisotropicRotation * M_2PI, point(0.0, 0.0, 0.0), Normal);

  if (diffuse_weight > 1e-5) {
    if (Subsurface > 1e-5) {
      color mixed_ss_base_color = SubsurfaceColor * Subsurface + BaseColor * (1.0 - Subsurface);
      if (subsurface_method == "burley") {
        BSDF = mixed_ss_base_color * bssrdf("principled",
                                            Normal,
                                            Subsurface * SubsurfaceRadius,
                                            SubsurfaceColor,
                                            "roughness",
                                            Roughness);
      }
      else {
        BSDF = mixed_ss_base_color * bssrdf("principled_random_walk",
                                            Normal,
                                            Subsurface * SubsurfaceRadius,
                                            mixed_ss_base_color,
                                            "roughness",
                                            Roughness);
      }
    }
    else {
      BSDF = BaseColor * principled_diffuse(Normal, Roughness);
    }

    if (Sheen > 1e-5) {
      color sheen_color = color(1.0, 1.0, 1.0) * (1.0 - SheenTint) + m_ctint * SheenTint;

      BSDF = BSDF + sheen_color * Sheen * principled_sheen(Normal);
    }

    BSDF = BSDF * diffuse_weight;
  }

  if (specular_weight > 1e-5) {
    float aspect = sqrt(1.0 - Anisotropic * 0.9);
    float r2 = Roughness * Roughness;

    float alpha_x = r2 / aspect;
    float alpha_y = r2 * aspect;

    color tmp_col = color(1.0, 1.0, 1.0) * (1.0 - SpecularTint) + m_ctint * SpecularTint;

    color Cspec0 = (Specular * 0.08 * tmp_col) * (1.0 - Metallic) + BaseColor * Metallic;

    if (distribution == "GGX" || Roughness <= 0.075) {
      BSDF = BSDF + specular_weight *
                        microfacet_ggx_aniso_fresnel(Normal,
                                                     T,
                                                     alpha_x,
                                                     alpha_y,
                                                     (2.0 / (1.0 - sqrt(0.08 * Specular))) - 1.0,
                                                     BaseColor,
                                                     Cspec0);
    }
    else {
      BSDF = BSDF + specular_weight * microfacet_multi_ggx_aniso_fresnel(
                                          Normal,
                                          T,
                                          alpha_x,
                                          alpha_y,
                                          (2.0 / (1.0 - sqrt(0.08 * Specular))) - 1.0,
                                          BaseColor,
                                          Cspec0);
    }
  }

  if (final_transmission > 1e-5) {
    color Cspec0 = BaseColor * SpecularTint + color(1.0, 1.0, 1.0) * (1.0 - SpecularTint);
    float eta = backfacing() ? 1.0 / f : f;

    if (distribution == "GGX" || Roughness <= 5e-2) {
      float cosNO = dot(Normal, I);
      float Fr = fresnel_dielectric_cos(cosNO, eta);

      float refl_roughness = Roughness;
      if (Roughness <= 1e-2)
        refl_roughness = 0.0;

      float transmission_roughness = refl_roughness;
      if (distribution == "GGX")
        transmission_roughness = 1.0 - (1.0 - refl_roughness) * (1.0 - TransmissionRoughness);

      BSDF = BSDF +
             final_transmission *
                 (Fr * microfacet_ggx_fresnel(
                           Normal, refl_roughness * refl_roughness, eta, BaseColor, Cspec0) +
                  (1.0 - Fr) * BaseColor *
                      microfacet_ggx_refraction(
                          Normal, transmission_roughness * transmission_roughness, eta));
    }
    else {
      BSDF = BSDF +
             final_transmission * microfacet_multi_ggx_glass_fresnel(
                                      Normal, Roughness * Roughness, eta, BaseColor, Cspec0);
    }
  }

  if (Clearcoat > 1e-5) {
    BSDF = BSDF + principled_clearcoat(
                      ClearcoatNormal, Clearcoat, ClearcoatRoughness * ClearcoatRoughness);
  }
}
